Structure ScanResults
  address.s
  numValue.l
  textValue.s
EndStructure

Structure Code
  address.s
  value.s
  type.s
EndStructure

Structure MemBlock
  baseAddress.l
  regionSize.l
EndStructure


Global NewList PrevFoundAddresses.ScanResults()
Global NewList FoundAddresses.ScanResults()
Global NewList ActiveCodeList.Code()
Global refreshTimer.f=1000.0    ;timer for refreshing cheat list values.
;Global hProcess, retPID

;used to pass back search parameters to scan procs
Structure SearchParams
  searchType.l
  compareType.l
  findValue.s
  findValueLength.l
  txtMode.l
EndStructure

Enumeration ;Search type
  #SRCH_NOTSET
  #SRCH_TEXT
  #SRCH_VALUE
EndEnumeration

Enumeration ;Compare Types
  #CMP_NOTSET
  #CMP_EQUALS
  #CMP_GREATER
  #CMP_LESS
  #CMP_BETWEEN
  #CMP_UNKNOWN
EndEnumeration

Enumeration ;text type flag
  #TXT_NOTSET
  #TXT_UNICODE
EndEnumeration

IncludeFile "memscan_common.pb"
IncludeFile "window_functions.pbi"


;----------------------
;-Peek Functions
;----------------------
Procedure.s memReadLong(address.s)
  Protected tmp$
  *sBuffer=AllocateMemory(4)
  pRET=ReadProcessMemory_(hProcess, Val("$"+address), *sBuffer, 4, @written)
  tmp$=Str(PeekL(*sBuffer))
  FreeMemory(*sBuffer)
  
  ProcedureReturn tmp$
EndProcedure

Procedure.s memReadString(address.s, length.l)
  Protected tmp$
  
  *sBuffer=AllocateMemory(length*2)
  pRET=ReadProcessMemory_(hProcess, Val("$" + address), *sBuffer, length*2, @written)
  tmp$=PeekS(*sBuffer, length, #PB_Unicode)
  FreeMemory(*sBuffer)
  
  ProcedureReturn tmp$
EndProcedure

;----------------------
;-Poke Functions
;----------------------
Procedure memWriteLong(address.s, value.l)
  pRET=WriteProcessMemory_(hProcess, Val("$"+address), @value, 4, 0)
  If pRET=0
    ;procedure failed return 0
    MessageRequester("memWriteLong() Failed!", "PokeLong() failed error code: " + Str(GetLastError_()), #MB_ICONERROR)
    ProcedureReturn 0
  EndIf
  ProcedureReturn pRET
EndProcedure

Procedure memWriteString(address.s, value.s)
  pRET=WriteProcessMemory_(hProcess, address, value.s, Len(value), 0)
  If pRET=0
    MessageRequester("PokeString() Failed!", "PokeString() failed error code: " + Str(GetLastError_()), #MB_ICONERROR)
    ProcedureReturn 0
  EndIf
  ProcedureReturn pRET
EndProcedure

;-======================================
Procedure NewScan()
  ;clear our current list
  ClearList(FoundAddresses())
  
  ;clear listboxes
  ClearGadgetItems(#MAIN_LISTBOX_ADDRESSES)
  ClearGadgetItems(#MAIN_LISTBOX_CODELIST)
  
  ;set value field as active and highlight it
  SetActiveGadget(#MAIN_TXT_VALUE)
  SendMessage_(GadgetID(#MAIN_TXT_VALUE), #EM_SETSEL, 0, -1)
  
  ;disable next scan button
  DisableGadget(#MAIN_NEXTSCN, 1)
EndProcedure

;-======================================
Procedure GetScanParameters()
  
  ;need to determine the search conditions.
  Protected scanType$=GetGadgetText(#MAIN_COMBO_SCAN_TYPE)
  Protected valueType$=GetGadgetText(#MAIN_COMBO_VALUE_TYPE)
  Protected value$=GetGadgetText(#MAIN_TXT_VALUE)
  Protected searchType = #SRCH_NOTSET
  Protected compareType = #CMP_NOTSET
  Protected len=0
  
  ;check value type first
  Select valueType$
    Case "Binary"
      searchType=#SRCH_VALUE
    Case "Byte"
      searchType=#SRCH_VALUE
      len=1
    Case "2 Bytes"
      searchType=#SRCH_VALUE  
      len=2
    Case "4 Bytes"
      searchType=#SRCH_VALUE  
      len=4
    Case "8 Bytes"
      searchType=#SRCH_VALUE  
      len=8
    Case "Double"
      searchType=#SRCH_VALUE  
    Case "Text"
      searchType=#SRCH_TEXT
      compareType = #CMP_EQUALS
      len=Len(value$)
    Case "Array of Bytes"
      ;nothing yet
    Case "All - Byte to Double"
      ;nothing yet
    Case "Custom"
      ;nothing yet
  EndSelect
  
  ;if compare type isn't already set-obtain it
  If Not compareType
    Select scanType$
      Case "Exact Value"
        compareType = #CMP_EQUALS
      Case "Greater Than..."
        compareType = #CMP_GREATER
      Case "Less Than..."
        compareType = #CMP_LESS
      Case "Value Between..."
        compareType = #CMP_BETWEEN
      Case "Unknown Initial Value"
        compareType = #CMP_UNKNOWN
    EndSelect
  EndIf
  
  ;if no compare type was found exit
  If compareType = #CMP_NOTSET
    MessageRequester("Error: No Compare Type", "Could not determine compare type.", #MB_ICONERROR)
    ProcedureReturn -1
  EndIf
  
  ;if no search type was found exit
  If searchType = #SRCH_NOTSET
    MessageRequester("Error: No Search Type", "Could not determine search type.", #MB_ICONERROR)
    ProcedureReturn -1
  EndIf  
  
  
  
  Static retParams.SearchParams
  retParams\compareType=compareType
  retParams\searchType=searchType
  retParams\findValue=value$
  retParams\findValueLength=len
  retParams\txtMode=GetGadgetState(#MAIN_CHK_UNICODE)
  
  ProcedureReturn @retParams
EndProcedure

;-======================================
Procedure InitialScan(*scan.SearchParams)
 
  Protected tmp$  
  Protected address=0
  Protected maxAddress=$7FFFFFFF
  Protected results=0
  Protected len = Len(*scan\findValue)
  Protected numVal=Val(*scan\findValue)
  Protected NewList ValidMemLocations.MemBlock()
  
  ;clear listbox
  ClearGadgetItems(#MAIN_LISTBOX_ADDRESSES)
  
  ;set progress bounds
  SetGadgetAttribute(#ProgressBar, #PB_ProgressBar_Minimum, address)
  SetGadgetAttribute(#ProgressBar, #PB_ProgressBar_Maximum, maxAddress)

  time.f=ElapsedMilliseconds()
  Repeat
    result=VirtualQueryEx_(hProcess, address, @mbi.MEMORY_BASIC_INFORMATION, SizeOf(MEMORY_BASIC_INFORMATION))
    If mbi\State = #MEM_COMMIT And mbi\Protect <> #PAGE_READONLY And mbi\Protect <> #PAGE_EXECUTE_READ And mbi\Protect <> #PAGE_GUARD And mbi\Protect <> #PAGE_NOACCESS
      AddElement(ValidMemLocations())
      ValidMemLocations()\baseAddress=address
      ValidMemLocations()\regionSize=mbi\RegionSize
    EndIf  
    address=mbi\BaseAddress+mbi\RegionSize
  Until  address >= maxAddress Or result=0
  MessageRequester("scan took:" , StrF(ElapsedMilliseconds()-time))
  
  *tmp=AllocateMemory(8)
  bob=*tmp
  
  
  time.f=ElapsedMilliseconds()
  ForEach ValidMemLocations()
    sBuffer=AllocateMemory(ValidMemLocations()\regionSize)
    res=ReadProcessMemory_(hProcess, ValidMemLocations()\baseAddress, sBuffer,ValidMemLocations()\regionSize, @written)
    
    ;at this point if written > 0 memory has been read , we just need to determine how to read it based gui elements
    If written > 0
      If *scan\searchType=#SRCH_TEXT
        For x = 0 To written
          ;string method - use to find text
          ;================
          If *scan\txtMode=#TXT_NOTSET
            tmp$=PeekS(sBuffer+x, *scan\findValueLength, #PB_Ascii) ;string length ! important
          Else
            tmp$=PeekS(sBuffer+x, *scan\findValueLength, #PB_Unicode) ;string length ! important
          EndIf  
        
          ;faster string compare by thorium see forum(http://www.purebasic.fr/english/viewtopic.php?f=12&t=44754&start=0)
          ;*bleh=@*scan\findValue
          ;If CompareMemory(sBuffer + x, *bleh, *scan\findValueLength)
            ;Debug "FOUND MATCH - " + Hex(*mbi\BaseAddress+x) + "=" + PeekS(sBuffer + x, len)
          ;EndIf
          If FindString(tmp$, *scan\findValue, 1)
            ;-SCAN: Found Result (Text)
            ;add to control
            ;AddGadgetItem(#MAIN_LISTBOX_ADDRESSES, -1, Hex(mbi\BaseAddress+x) + Chr(10) + tmp$)
          
            ;update our linked list
            AddElement(FoundAddresses())
            FoundAddresses()\address=Hex(ValidMemLocations()\baseAddress+x)
            FoundAddresses()\textValue=tmp$
          
            ;add 1 to result count
            results+1 
            Continue    ;go to next byte
            ;Debug "FOUND MATCH - " + Hex(mbi\BaseAddress+x) + "=" + tmp$
          EndIf
        Next
      EndIf  
      
      If *scan\searchType = #SRCH_VALUE
        ;volitile registers
        ;-EAX
        ;-EDX
        ;-ECX
        count=0
        start:
        ! MOV EDX, [p.v_sBuffer]      ;EAX holds buffer address
        ! ADD EDX, dword [p.v_count]  ;increment location of sBuffer by count
        ! MOV EAX, dword [EDX]        ;grab the address of sBuffer+count and put it in EAX        
        ! MOV [p.p_tmp], EDX
        ! CMP EAX, dword [p.v_numVal]                ;compare EAX with 555
        ! JE l_add                    ;if it is equal then add this to a match
        cont:
        ! ADD dword [p.v_count], 1
        ! MOV ECX, [p.v_count]
        ! CMP ECX, [p.v_written]
        ! JNE l_start
        
         For x = 0 To 0;written
           
           ;number method - use To find number value
           ;Setting up scan value length
           Select *scan\findValueLength
             Case 2
               tmp=PeekW(sBuffer+x)
             Case 4
               tmp=PeekL(sBuffer+x)
             Case 8
               tmp=PeekQ(sBuffer+x)
             ;case double
             ;case float
;               ;case xxxx
           EndSelect
           
          ;tmp=PeekL(sBuffer+x)
          If tmp = numVal
            ;If tmp=Val(*scan\findValue)
            ;-SCAN: Found Result (Value)
            ;add result to control
            ;AddGadgetItem(#MAIN_LISTBOX_ADDRESSES, -1, Hex(mbi\BaseAddress+x) + Chr(10) + Str(tmp))
            If 2 = 6;LOL!
            add:
            ;tmp update our linked List
             ! ADD dword [p.v_count], 4
             AddElement(FoundAddresses())
             FoundAddresses()\address=Hex(ValidMemLocations()\baseAddress+count)
             FoundAddresses()\numValue=PeekL(*tmp)
             results+1
             ! JMP l_cont
            EndIf 
            ;update our linked list
;               AddElement(FoundAddresses())
;               FoundAddresses()\address=Hex(mbi\BaseAddress+x)
;               FoundAddresses()\numValue=tmp
      
            ;add 1 to result count
            ;results+1
            Continue ;goto next byte
            ;Debug "FOUND MATCH - " + Hex(mbi\BaseAddress + x) + "=" + Str(tmp)
          EndIf
         Next  
      EndIf  
    EndIf 
    FreeMemory(sBuffer)
    ;FreeMemory(*tmp)  
    ;update the progress bar during loop
    SetGadgetState(#ProgressBar, ValidMemLocations()\baseAddress)
  Next
  
  If bob
    FreeMemory(bob)
  EndIf  
  
  MessageRequester("scan took:" , StrF(ElapsedMilliseconds()-time))
  
  
  If results < 200
    Select *scan\searchType
      Case #SRCH_VALUE
        ForEach FoundAddresses()
          AddGadgetItem(#MAIN_LISTBOX_ADDRESSES, -1, FoundAddresses()\address + Chr(10) + Str(FoundAddresses()\numValue))
        Next
  
      Case #SRCH_TEXT
        ForEach FoundAddresses()
          AddGadgetItem(#MAIN_LISTBOX_ADDRESSES, -1, FoundAddresses()\address + Chr(10) + FoundAddresses()\textValue)
        Next
    EndSelect  
  EndIf  
  
  
  ;beep done
  MessageBeep_(#MB_OK)
  
  ;set label to show results
  SetGadgetText(#MAIN_LBL_FOUND, "Found: " + Str(results))
  
  ;reset progress bar at end
  SetGadgetState(#ProgressBar, 0)
  
  ProcedureReturn results
EndProcedure
;-======================================

Procedure NextScan(*scan.SearchParams)
  ;depending on scan params we read thru FoundAddresses() list and compare values
  Protected results = 0
  Protected readValue$
  NewList tmp.ScanResults()
  
  If *scan\searchType = #SRCH_TEXT  
    ForEach FoundAddresses()
      ;read thru original list and try to find a matching value
      readValue$=memReadString(FoundAddresses()\address, *scan\findValueLength)
      If readValue$ = *scan\findValue
        ;we have found a match in our old list
        ;lets add this result to the new (tmp) list
        AddElement(tmp())
        tmp()\address=FoundAddresses()\address
        tmp()\textValue=FoundAddresses()\textValue
        
        ;add to results counter
        results + 1
      EndIf
    Next
  EndIf
  
  If *scan\searchType = #SRCH_VALUE
    ForEach FoundAddresses()
      ;read thru original list and try to find a matching value
      readValue$=memReadLong(FoundAddresses()\address)
      If readValue$ = *scan\findValue
        ;we have found a match in our old list
        ;lets add this result to the new (tmp) list
        AddElement(tmp())
        tmp()\address=FoundAddresses()\address
        tmp()\numValue=Val(readValue$)
        
        ;add to results counter
        results + 1
      EndIf
    Next
  EndIf
  
    
  ;copy our first list to prev (for undo operation)
  CopyList(FoundAddresses(), PrevFoundAddresses())
  
  ;clear the first list
  ClearList(FoundAddresses())
  
  ;copy the tmp (new list) to our current list
  CopyList(tmp(), FoundAddresses())
  
  ;can release tmp list
  FreeList(tmp())
  
  ;clear our control
  ClearGadgetItems(#MAIN_LISTBOX_ADDRESSES)
  
  ;-TO DO - fill when results < certain amt
  ;Do not actually want to fill the control all the time.  Depending on how many results we find we can fill it.
  ;Filling the control when there's a lot of results will lag the system.
  
  
  If results < 1000
    Select *scan\searchType
      Case #SRCH_VALUE
        ForEach FoundAddresses()
          AddGadgetItem(#MAIN_LISTBOX_ADDRESSES, -1, FoundAddresses()\address + Chr(10) + Str(FoundAddresses()\numValue))
        Next
  
      Case #SRCH_TEXT
        ForEach FoundAddresses()
          AddGadgetItem(#MAIN_LISTBOX_ADDRESSES, -1, FoundAddresses()\address + Chr(10) + FoundAddresses()\textValue)
        Next
    EndSelect  
  EndIf  
    
  ;update our label
  SetGadgetText(#MAIN_LBL_FOUND, "Found: " + Str(results))
EndProcedure

;-======================================
Procedure UpdateCheatList()
  ;re pokes values
  
  ;loop listbox
  ;read items
  ;determine poke type
  ;poke values
  
  ForEach ActiveCodeList()
    Select ActiveCodeList()\type
      Case "4 Bytes"
        memWriteLong(ActiveCodeList()\address, Val(ActiveCodeList()\value))
    EndSelect
  Next
  
EndProcedure

Procedure UpdateAddressList()
  ClearGadgetItems(#MAIN_LISTBOX_ADDRESSES)
  ;instead of clearing all of them, need to just read the listbox
  ;and update accordingly.
  ForEach FoundAddresses()
    
    ;only works for longs atm
    FoundAddresses()\numValue=Val(memReadLong(FoundAddresses()\address))
    AddGadgetItem(#MAIN_LISTBOX_ADDRESSES, -1, FoundAddresses()\address + Chr(10) + Str(FoundAddresses()\numValue))
  Next
  
EndProcedure


;Open our main window and get ready to process events
CallMainWindow()

;----Starting Update Timer
updateTime.f=ElapsedMilliseconds()

Repeat
  If ElapsedMilliseconds()-updateTime>refreshTimer
    ;reset timer
    updateTime=ElapsedMilliseconds()
    
    ;get count of gadgets
    cnt = CountGadgetItems(#MAIN_LISTBOX_CODELIST)
    
    If cnt
      UpdateCheatList()
    EndIf
    
    cnt = CountGadgetItems(#MAIN_LISTBOX_ADDRESSES)
    
    If cnt
      UpdateAddressList()
    EndIf
    
    
  EndIf
  
  event = WaitWindowEvent(1)
  gadget = EventGadget()
  window = EventWindow()
  
  ;-======================================
  ;Main Window Event Handler
  ;-WINDOW: Main Window
  If window = #MAIN_WINDOW
    Select event
      Case #PB_Event_Gadget
        ;-BUTTON: Open Processes
        If gadget = #MAIN_BTN_OPEN_PROCS
          ;call process chooser window
            CallProcChooser()
        EndIf
        
        ;-BUTTON: First Scan
        If gadget = #MAIN_BTN_FIRSTSCN
          If retPID = 0
            MessageRequester("Error: No Process", "No process has been chosen!", #MB_ICONINFORMATION)
            Continue
          EndIf
          
          If Not scanned
            tmp$=GetGadgetText(#MAIN_TXT_VALUE)
            If Len(tmp$) > 0
              scanned = 1 ;flag to determine first/new scan button
              ;set to new scan button
              SetGadgetText(#MAIN_BTN_FIRSTSCN, "New Scan")
              *scan.SearchParams = GetScanParameters()
              If InitialScan(*scan)
                ;enable next scan button
                DisableGadget(#MAIN_NEXTSCN, 0)
              EndIf
            Else
              MessageRequester("Error: No value", "No value has been entered.", #MB_ICONINFORMATION)
            EndIf
          ElseIf scanned
            tmp$=GetGadgetText(#MAIN_TXT_VALUE)
            If Len(tmp$) > 0
              scanned = 0 
              SetGadgetText(#MAIN_BTN_FIRSTSCN, "First Scan")
              NewScan()
            Else
              MessageRequester("Error: No value", "No value has been entered.", #MB_ICONINFORMATION)
            EndIf
          EndIf 
        EndIf
        
        ;-BUTTON: Next Scan
        If gadget = #MAIN_NEXTSCN
          *scan.SearchParams = GetScanParameters()
          If *scan
            NextScan(*scan)
          EndIf
        EndIf
        
        ;-BUTTON: C Button(testing)
        If gadget = #MAIN_BTN_COPYALL
          Debug memReadLong("$407C38")
        EndIf  
        
        ;-COMBOBOX: Value Type
        If gadget = #MAIN_COMBO_VALUE_TYPE
          Select GetGadgetText(#MAIN_COMBO_VALUE_TYPE)
            Case "Text"
              ;if text is selected, show chkboxes
              HideGadget(#MAIN_CHK_CASE, 0)
              HideGadget(#MAIN_CHK_UNICODE, 0)
            Default
              HideGadget(#MAIN_CHK_CASE, 1)
              HideGadget(#MAIN_CHK_UNICODE, 1)
          EndSelect
        EndIf
        
        ;-BUTTON: Add Address
        If gadget = #MAIN_BTN_ADD_ADDR
          addingCode=#True    ;flag to determine if adding new code or editing
          CallCodeEditWindow()
        EndIf
        
      Case #PB_Event_SizeWindow
        ;makes sure bottom listbox covers entire bottom area
        ResizeGadget(#MAIN_LISTBOX_CODELIST, GadgetX(#MAIN_LISTBOX_CODELIST), GadgetY(#MAIN_LISTBOX_CODELIST), WindowWidth(#MAIN_WINDOW), GadgetHeight(#MAIN_LISTBOX_CODELIST) + (WindowHeight(#MAIN_WINDOW) - GadgetY(#MAIN_LISTBOX_CODELIST)))
        
    EndSelect
    
    ;======================================================
    ;Handle Double Clicking Address List
    ;-LIST ICON GADGET: Addresses
    If event = #WM_LBUTTONDBLCLK And gadget = #MAIN_LISTBOX_ADDRESSES
      ;get the selected item of the addresses list box
      item = GetGadgetState(#MAIN_LISTBOX_ADDRESSES)
      selectedAddress$=GetGadgetItemText(#MAIN_LISTBOX_ADDRESSES, item, 0) ;0 for address column
      selectedValue$=GetGadgetItemText(#MAIN_LISTBOX_ADDRESSES, item, 1) ; 1 for value column
      
      ;make sure something is selected
      If selectedAddress$ <> "Address"
        ;add this to the code list                    description               address                                type                                   value
        AddGadgetItem(#MAIN_LISTBOX_CODELIST, -1, "[Description]" + Chr(10) + selectedAddress$ + Chr(10) + GetGadgetText(#MAIN_COMBO_VALUE_TYPE) + Chr(10) + selectedValue$)
      EndIf  
    EndIf
    ;=====================================================
    
    ;=====================================================
    ;Handle Double Clicking Code List
    ;-LIST ICON GADGET: Code List
    If event = #WM_LBUTTONDBLCLK And gadget = #MAIN_LISTBOX_CODELIST
      ;get the selected item of the code list
      item = GetGadgetState(#MAIN_LISTBOX_CODELIST)
      selectedDesc$=GetGadgetItemText(#MAIN_LISTBOX_CODELIST, item, 0)       ;1 for desc
      selectedAddress$=GetGadgetItemText(#MAIN_LISTBOX_CODELIST, item, 1)    ;2 for address column
      selectedType$=GetGadgetItemText(#MAIN_LISTBOX_CODELIST, item, 2)       ;3 for type
      selectedValue$=GetGadgetItemText(#MAIN_LISTBOX_CODELIST, item, 3)      ;4 for value column
      
      If selectedAddress$ <> "Address"
        ;call our code edit window
        addingCode=#False
        CallCodeEditWindow()
      
        SetGadgetText(#EDIT_TXT_DESC, selectedDesc$)
        SetGadgetText(#EDIT_TXT_ADDRESS, selectedAddress$)
        SetGadgetText(#EDIT_TXT_VALUE, selectedValue$)
      EndIf  
    EndIf      
    
    If event = #WM_LBUTTONDOWN And gadget = #MAIN_LISTBOX_CODELIST
      item=GetGadgetState(#MAIN_LISTBOX_CODELIST)
      res=GetGadgetItemState(#MAIN_LISTBOX_CODELIST, item)
      
      Debug SendMessage_(GadgetID(gadget), #LVM_GETITEMSTATE, item, #LVIS_STATEIMAGEMASK) >> 12
      If res & #PB_ListIcon_Checked And checked=0
        checked=1
        Debug "checked"
      ElseIf Not res & #PB_ListIcon_Checked And checked=1
        checked=0
        Debug "unchecked"
      EndIf
    EndIf
    ;=====================================================
    
  EndIf
  ;End Main Window Event Handler
  ;======================================
  
  ;-======================================
  ;Process List Event Handler
  ;-WINDOW: Process List
  If window = #PROC_WINDOW
    Select event
      Case #PB_Event_Gadget
        ;if clicked accept button, do choose routine
        ;-BUTTON: Select
        If gadget = #PROC_BTN_SELECT
          retPID = ChoosePID()
        EndIf
        
        ;-BUTTON: Cancel
        If gadget = #PROC_BTN_CANCEL
          DisableWindow(#MAIN_WINDOW, 0)
          CloseWindow(#PROC_WINDOW)
          retPID = 0
        EndIf  
        
      ;-LIST VIEW: Process List
      Case #WM_LBUTTONDBLCLK
        ;if double clicked procs list do choose routine
        If gadget = #PROC_LIST_PROCS
          retPID = ChoosePID()
        EndIf
        
      Case #PB_Event_CloseWindow
        DisableWindow(#MAIN_WINDOW, 0)
        CloseWindow(#PROC_WINDOW)
    EndSelect
  EndIf
  ;End Process List Event Handler
  ;=====================================
  
  
  ;-======================================
  ;Code edit window event handler
  ;-WINDOW: Code Edit
  If window = #CODE_EDIT_WINDOW
    Select event
      Case #PB_Event_Gadget
        ;-BUTTON Cancel
        If gadget = #EDIT_BTN_CANCEL
          DisableWindow(#MAIN_WINDOW, 0)
          CloseWindow(#CODE_EDIT_WINDOW)
        EndIf
        
        ;-BUTTON Save
        If gadget = #EDIT_BTN_SAVE
          ;get form values
          item=GetGadgetState(#MAIN_LISTBOX_CODELIST)
          enteredAddress$=GetGadgetText(#EDIT_TXT_ADDRESS)
          enteredValue$=GetGadgetText(#EDIT_TXT_VALUE)
          enteredDesc$=GetGadgetText(#EDIT_TXT_DESC)
          enteredType$=GetGadgetText(#EDIT_COMBO_VALUE_TYPE)
          frozen=GetGadgetState(#EDIT_CHK_FREEZE)
          type=GetValueType(enteredType$)
          ;determine if we're adding a new code or editing
          If addingCode     ;addingCode flag set by clicking the button "Manually Add Code"
            ;add new code to list
            If enteredAddress$ <> "" And enteredValue$ <> ""
              AddGadgetItem(#MAIN_LISTBOX_CODELIST, -1, enteredDesc$ + Chr(10) + enteredAddress$ + Chr(10) + enteredType$ + Chr(10) + enteredValue$ + Chr(10) + "Not Frozen")
              DisableWindow(#MAIN_WINDOW, 0)
              CloseWindow(#CODE_EDIT_WINDOW)
            Else
              MessageRequester("Error: Missing data", "Address or value is missing.", #MB_ICONINFORMATION)
            EndIf  
          Else  ;if editing 
            ;edit current code
            If enteredAddress$ <> "" And enteredValue$ <> ""
              SetGadgetItemText(#MAIN_LISTBOX_CODELIST, item, enteredDesc$, 0)
              SetGadgetItemText(#MAIN_LISTBOX_CODELIST, item, enteredAddress$, 1)
              SetGadgetItemText(#MAIN_LISTBOX_CODELIST, item, enteredType$, 2)
              SetGadgetItemText(#MAIN_LISTBOX_CODELIST, item, enteredValue$, 3)
              DisableWindow(#MAIN_WINDOW, 0)
              CloseWindow(#CODE_EDIT_WINDOW)
            Else
              MessageRequester("Error: Missing data", "Address or value is missing.", #MB_ICONINFORMATION)
            EndIf  
          EndIf
          
          ;if freeze is checked, poke that value -- todo later -- completely freeze it by poking on timer
          If frozen
            ;at this point, or prior we need to determine what type of memory write function to use, probably at the top of this msg, after retrieving values.
            Select type
              Case #SRCH_VALUE
                ; get what kind of type donkey for now hardcoded to only longs
                writeConfirm=memWriteLong(enteredAddress$, Val(enteredValue$))
                
              Case #SRCH_TEXT
                writeConfirm=memWriteString(enteredAddress$, enteredValue$)
              Default
            EndSelect  
            
            ;return value from write process memory, anything but 1 is a fail i believe.
            If writeConfirm
              SetGadgetItemText(#MAIN_LISTBOX_CODELIST, item, "Frozen/Poked", 4)
              AddElement(ActiveCodeList())
              ActiveCodeList()\address=enteredAddress$
              ActiveCodeList()\type=enteredType$
              ActiveCodeList()\value=enteredValue$
            Else
              SetGadgetItemText(#MAIN_LISTBOX_CODELIST, item, "Poke Failed", 4)
            EndIf  
          Else
            ;the item was just added
            SetGadgetItemText(#MAIN_LISTBOX_CODELIST, item, "Not Frozen", 4)
          EndIf
          
        EndIf
        
      Case #PB_Event_CloseWindow
        DisableWindow(#MAIN_WINDOW, 0)
        CloseWindow(#CODE_EDIT_WINDOW)
    EndSelect
  EndIf
  ;=====================================
 
Until event = #PB_Event_CloseWindow And window = #MAIN_WINDOW

Debug retPID

; IDE Options = PureBasic 4.51 (Windows - x86)
; CursorPosition = 259
; FirstLine = 251
; Folding = ---
; EnableAsm
; EnableThread
; EnableXP
; UseIcon = memory.ico
; Watchlist = InitialScan()>address;hProcess;InitialScan()>x;InitialScan()>count